﻿using System;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using RSA.Core;

namespace RSA.Front
{
    public partial class RsaMainForm : Form
    {
        private readonly string _keyDirectory = Directory.GetParent(Application.StartupPath).Parent.Parent.FullName;
        private byte[] _encryptedBytes;
        public RsaMainForm()
        {
            InitializeComponent();

            if (File.Exists(_keyDirectory + "\\Keys\\PrivateKey.xml"))
                PrivateKeyTextBox.Text = _keyDirectory + "\\Keys\\PrivateKey.xml";

            if (File.Exists(_keyDirectory + "\\Keys\\PublicKey.xml"))
                PublicKeyTextBox.Text = _keyDirectory + "\\Keys\\PublicKey.xml";

            if (File.Exists(_keyDirectory + "\\Keys\\PrivateKey.xml"))
                EnPrivateKeyPathEdit.Text = _keyDirectory + "\\Keys\\PrivateKey.xml";

            if (File.Exists(_keyDirectory + "\\Keys\\PublicKey.xml"))
                DePublicKeyPath.Text = _keyDirectory + "\\Keys\\PublicKey.xml";
        }
        #region - Event Handlers -
        private void PrivateKeyBrowseButton_Click(object sender, EventArgs e)
        {
            using (var openFile = new OpenFileDialog
                                      {
                                          Filter = "XML files (*.xml)|",
                                          FileName = (File.Exists(_keyDirectory + "\\Keys\\PrivateKey.xml")) ?
                                            _keyDirectory + "\\Keys\\PrivateKey.xml" : "",
                                          InitialDirectory = (Directory.Exists(_keyDirectory + "\\Keys\\")) ?
                                            _keyDirectory + "\\Keys\\" : ""

                                      })
            {
                if (openFile.ShowDialog() == DialogResult.OK)
                {
                    PrivateKeyTextBox.Text = openFile.FileName;
                    EnPrivateKeyPathEdit.Text = openFile.FileName;
                }
            }
        }

        private void PublicKeyPathButton_Click(object sender, EventArgs e)
        {
            using (var openFile = new OpenFileDialog
                                      {
                                          Filter = "XML files (*.xml)|",
                                          FileName = (File.Exists(_keyDirectory + "\\Keys\\PublicKey.xml")) ?
                                            _keyDirectory + "\\Keys\\PublicKey.xml" : "",
                                          InitialDirectory = (Directory.Exists(_keyDirectory + "\\Keys\\")) ?
                                            _keyDirectory + "\\Keys\\" : ""
                                      })
            {
                if (openFile.ShowDialog() == DialogResult.OK)
                {
                   PrivateKeyTextBox.Text = openFile.FileName;
                   DePublicKeyPath.Text = openFile.FileName;
                }
            }
        }

        private void GenerateButton_Click(object sender, EventArgs e)
        {
            if (PrivateKeyTextBox.Text == "")
            {
                MessageBox.Show("Private key path could not be empty", "Error");
                return;
            }
            if (PublicKeyTextBox.Text == "")
            {
                MessageBox.Show("Public key path could not be empty", "Error");
                return;
            }
            GenerateKeys((int)KeyLengthUpDown.Value, PrivateKeyTextBox.Text, PublicKeyTextBox.Text);
        }
        private void EnBrowsePrivateKeyButton_Click(object sender, EventArgs e)
        {
            using (var openFile = new OpenFileDialog
            {
                Filter = "XML files (*.xml)|",
                FileName = (File.Exists(_keyDirectory + "\\Keys\\PrivateKey.xml")) ?
                  _keyDirectory + "\\Keys\\PrivateKey.xml" : "",
                InitialDirectory = (Directory.Exists(_keyDirectory + "\\Keys\\")) ?
                  _keyDirectory + "\\Keys\\" : ""

            })
            {
                if (openFile.ShowDialog() == DialogResult.OK)
                    EnPrivateKeyPathEdit.Text = openFile.FileName;
            }
        }

        private void EncryptButton_Click(object sender, EventArgs e)
        {
            if(TextToEncryptEdit.Text == "")
            {
                MessageBox.Show("Enter text to encrypt");
                return;
            }
            if (EnPrivateKeyPathEdit.Text == "")
            {
                MessageBox.Show("Private key path could not be empty", "Error");
                return;
            }
            EncryptedTextEdit.Text = EncryptText(DePublicKeyPath.Text, TextToEncryptEdit.Text);
        }

        private void DePublicKeyBrowseButton_Click(object sender, EventArgs e)
        {
            using (var openFile = new OpenFileDialog
            {
                Filter = "XML files (*.xml)|",
                FileName = (File.Exists(_keyDirectory + "\\Keys\\PublicKey.xml")) ?
                  _keyDirectory + "\\Keys\\PublicKey.xml" : "",
                InitialDirectory = (Directory.Exists(_keyDirectory + "\\Keys\\")) ?
                  _keyDirectory + "\\Keys\\" : ""
            })
            {
                if (openFile.ShowDialog() == DialogResult.OK)
                    DePublicKeyPath.Text = openFile.FileName;
            }
        }

        private void DecryptButton_Click(object sender, EventArgs e)
        {
            if (EncryptedTextEdit.Text == "")
            {
                MessageBox.Show("Enter text to decrypt");
                return;
            }
            if (DePublicKeyPath.Text == "")
            {
                MessageBox.Show("Public key path could not be empty", "Error");
                return;
            }
            DecryptedTextEdit.Text = DecryptText(EnPrivateKeyPathEdit.Text);
        }

        #endregion

        #region - Tools -
        private void GenerateKeys(int keyLength, string privateKeyPath, string publicKeyPath)
        {
            CryptoKey privateKey;
            CryptoKey publicKey;
            ICryptoProvider cryptoProvider = new RSACryptoProvider();
            cryptoProvider.GenerateKeys(keyLength, out publicKey, out privateKey);

            WriteKey("PublicKey",publicKeyPath,publicKey);
            WriteKey("PrivateKey",privateKeyPath,privateKey);
            MessageBox.Show("Successfully finished");
        }
        private void WriteKey(string head, string path, CryptoKey key)
        {
            var xmlWriter = new StreamWriter(path);
            try
            {
            var doc = new XmlDocument();
            doc.LoadXml("<"+head+"></"+head+">");
            XmlNode root = doc.DocumentElement;

            var elemFactor = doc.CreateElement("Factor");
            elemFactor.InnerText = key.Factor.ToString();
            if (root != null) root.AppendChild(elemFactor);

            var elemModulus = doc.CreateElement("Modulus");
            elemModulus.InnerText = key.Modulus.ToString();
            if (root != null) root.AppendChild(elemModulus);

            xmlWriter.Write(doc.InnerXml);

            }
            finally
            {
                xmlWriter.Close();
            }
        }

        private string EncryptText(string keyPath, string text)
        {
            try
            {
                _encryptedBytes = null;
                var cryptoProvider = new RSACryptoProvider();
                var publicKey = GetKey(keyPath);
                var asciiEncoding = new ASCIIEncoding();
                var dataToEncrypt = asciiEncoding.GetBytes(text);
                _encryptedBytes = cryptoProvider.Encrypt(dataToEncrypt, publicKey);
                return asciiEncoding.GetString(_encryptedBytes);
            }
            catch (Exception)
            {
                return "";
            }
        }
        private string DecryptText(string keyPath)
        {
            var cryptoProvider = new RSACryptoProvider();
            var asciiEncoding = new ASCIIEncoding();
            var privateKey = GetKey(keyPath);
            var decryptedData = cryptoProvider.Decrypt(_encryptedBytes, privateKey);
            return asciiEncoding.GetString(decryptedData);
        }

        private CryptoKey GetKey(string filePath)
        {
            var sr = new StreamReader(filePath);
            try
            {
                var doc = new XmlDocument();
                doc.LoadXml(sr.ReadToEnd());
                return new CryptoKey(new BigInteger(doc.SelectSingleNode("*/Factor").InnerText, 10), new BigInteger(doc.SelectSingleNode("*/Modulus").InnerText, 10));
            }
            finally
            {
                sr.Close();
            }
        }

        #endregion

    }
}
